﻿using System;

namespace MockingBird.Simulator.Framework
{

    using System.IO;
    using System.Xml;
    using System.Xml.Linq;
    using Common.Infrastructure.Helpers;
    using Common.Infrastructure.Logging;
    using Interfaces;

    public class SoapResponseBuilder : IResponseBuilder
    {
        private ILogger logger;

        #region "constructor(s)"

        public SoapResponseBuilder(ILogger logger)
        {
            this.logger = logger;
        }

        #endregion

        /// <summary>
        /// Builds response SOAP envelope inserting body data and any SOAP headers
        /// </summary>
        /// <param name="bodyData"></param>
        /// <returns></returns>
        public XmlDocument BuildResponse(XmlDocument bodyData, XmlDocument responseHeader)
        {
            return BuildResponseFromXDoc(bodyData, responseHeader);
        }

        public XmlDocument BuildResponse(string bodyDataFile, XmlDocument responseHeader)
        {

            logger.LogInfo("SoapResponseBuilder: headerData file argument is ignored");

            if (String.IsNullOrEmpty(bodyDataFile))
                throw new ArgumentException("bodyDataFile is null or empty.", "bodyDataFile");

            if (!File.Exists(bodyDataFile))
                throw new FileNotFoundException("File could not be found", bodyDataFile);

            return BuildResponse(GetBodyData(bodyDataFile), responseHeader);

        }

        private XmlDocument GetBodyData(string bodyDataFile)
        {

            XmlDocument bodyData = new XmlDocument();
            try
            {
                bodyData.Load(bodyDataFile);
            }
            catch (XmlException xex)
            {
                logger.LogError("Xml exception encountered when attempting to load bodyData File :" + bodyDataFile, xex);
                throw;
            }
            return bodyData;
        }

        private XmlDocument BuildResponseFromXDoc(XmlDocument responseBody, XmlDocument responseHeader)
        {
            //Setup the namespaces
            XNamespace env = XNamespace.Get("http://schemas.xmlsoap.org/soap/envelope/");
            XNamespace xsd = XNamespace.Get("http://www.w3.org/2001/XMLSchema");
            XNamespace xsi = XNamespace.Get("http://www.w3.org/2001/XMLSchema-instance");

            //Setup the envelope with the namespaces
            XElement envelope = new XElement(env + "Envelope",
                new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema"),
                new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"));

            //Setup the body to be prefixed with the main namespace but no other attributes are needed
            //we have to construct the body from proper xml nodes and not just the responseBody outer xml
            XElement bodyInner = XmlHelper.GetXElement(responseBody);
            XElement body = new XElement(env + "Body", bodyInner);

            //setup any headers
            if (null != responseHeader && null != responseHeader.DocumentElement)
            {
                // Expect <Header>  <SomeXml/> </Header>
                XElement[] innerHeaders = new XElement[responseHeader.DocumentElement.ChildNodes.Count];

                int counter = 0;
                foreach (XmlNode h in responseHeader.DocumentElement.ChildNodes)
                {
                    innerHeaders[counter++] = XmlHelper.GetXElement(h);
                }

                XElement header = new XElement(env + "Header", innerHeaders);
                envelope.Add(header);
            }

            envelope.Add(body);

            //Load the envelope into the wrapped XDocument
            XDocument doc = new XDocument();
            doc.Add(envelope);

            //The XDocument contents need to be written out into an xmldocument.
            //for that we need to go via a memory stream and xmlwriter
            return XmlHelper.GetXmlDocFromXDoc(doc);
        }

    }
}
